//------------------------------------------------------------------------------
// Class: gewCamera

var CAMERA_UPDATE_VIEW_MATRIX = 1 << 8;
var CAMERA_UPDATE_PROJ_MATRIX = 1 << 9;
var CAMERA_UPDATE_VIEWPORT = 1 << 10;

function gewCamera() {
  gewNode.call(this);
  this.m_near = 0.0;
  this.m_far = 0.0;
  this.m_visual_far = 0.0; 
  this.m_fov = 0.0; // fiew of view
  this.m_aspect_ratio = 0.0;
  this.m_zoom_factor = 1.0;
  
  this.m_viewport_pos = new gewVec2(0.0, 0.0);
  this.m_viewport_size = new gewVec2(0.0, 0.0);
  
  this.m_up = new gewVec3(0.0, 1.0, 0.0);
  this.m_forward = new gewVec3(0.0, 0.0, 1.0);
  this.m_right = new gewVec3(1.0, 0.0, 0.0);
  
  this.m_look_at_position = new gewVec3(0.0, 0.0, 0.0);
  //this.m_up_vector = new gewVec3(0.0, 0.0, 0.0);
  
  // desired position of camera
  this.m_desired_pos = new gewVec3(0.0, 0.0, 0.0); // pos in world
  this.m_desired_fov = this.m_fov;
  // orient of this camera in Euler angles around world-coord 
  // this angle is in degree
  //this.m_desired_orient = new gewVec3(0.0, 0.0, 0.0);
  // orietation of camera
  //this.m_yaw = 0.0; // the rotation of camera around up-axis of the world
  //this.m_pitch = 0.0; // the rotation of camera around its right-axis
  //this.m_roll = 0.0; // the rotation around the forward heading of camera
  this.m_view_matrix = null;
  this.m_project_matrix = null;
  this.m_visual_project_matrix = null;
  this.m_frustum = null;
  this.m_cam_flags = CAMERA_UPDATE_VIEW_MATRIX | CAMERA_UPDATE_PROJ_MATRIX 
                     | CAMERA_UPDATE_VIEWPORT;
}
extend2(gewCamera, gewNode);

// mutator
gewCamera.prototype.setCameraFlag = function(bit_flag, value) {
  if (value === true) {
    this.m_cam_flags |= bit_flag;
  } else if (value === false){
    this.m_cam_flags &= ~bit_flag;
  }
};

gewCamera.prototype.setNear = function(near) {
  this.m_near = near;
  this.setCameraFlag(CAMERA_UPDATE_PROJ_MATRIX, true);
};

gewCamera.prototype.setFar = function(far,visual_far) {
  this.m_far = far;
  this.setCameraFlag(CAMERA_UPDATE_PROJ_MATRIX, true);
  if(visual_far)
    this.m_visual_far = visual_far;
  else
    this.m_visual_far = far;
};

gewCamera.prototype.setFov = function(fov) {
  this.m_fov = fov;
  this.setCameraFlag(CAMERA_UPDATE_PROJ_MATRIX, true);
};

gewCamera.prototype.setAspectRatio = function(aspect_ratio) {
  this.m_aspect_ratio = aspect_ratio;
  this.setCameraFlag(CAMERA_UPDATE_PROJ_MATRIX, true);
};

gewCamera.prototype.setViewport = function(x, y, width, height) {
  this.m_viewport_pos.x = x;
  this.m_viewport_pos.y = y;
  this.m_viewport_size.x = width;
  this.m_viewport_size.y = height;
  this.setCameraFlag(CAMERA_UPDATE_VIEWPORT, true);
  this.setCameraFlag(CAMERA_UPDATE_PROJ_MATRIX, true);
};

gewCamera.prototype.setUp = function(up){
  this.m_up.set(up.x,up.y,up.z);
  this.setCameraFlag(CAMERA_UPDATE_VIEW_MATRIX, true);
}
gewCamera.prototype.setDesiredPos = function(desired_pos){
  this.m_desired_pos.set(desired_pos.x,desired_pos.y,desired_pos.z);
  this.setCameraFlag(CAMERA_UPDATE_VIEW_MATRIX, true);
};

gewCamera.prototype.setYaw = function(yaw) {
  this.m_yaw = yaw;
  this.setCameraFlag(CAMERA_UPDATE_VIEW_MATRIX, true);
};

gewCamera.prototype.setPitch = function(pitch) {
  this.m_pitch = pitch;
  this.setCameraFlag(CAMERA_UPDATE_VIEW_MATRIX, true);
};

gewCamera.prototype.setRoll = function(roll) {
  this.m_roll = roll;
  this.setCameraFlag(CAMERA_UPDATE_VIEW_MATRIX, true);
};

gewCamera.prototype.setLookAt = function(v_position) {
  this.m_look_at_position.set(v_position.x,v_position.y,v_position.z);
  this.setCameraFlag(CAMERA_UPDATE_VIEW_MATRIX, true);
}

// accessor
gewCamera.prototype.getCameraFlag = function(bit_flag) {
  return this.m_cam_flags & bit_flag;
};

gewCamera.prototype.getNear = function() {
  return this.m_near;
};

gewCamera.prototype.getFar = function() {
  return this.m_far;
};

gewCamera.prototype.getFov = function() {
  return this.m_fov;
};

gewCamera.prototype.getAspectRatio = function() {
  return this.m_aspect_ratio;
};

gewCamera.prototype.getDesiredPos = function() {
  return this.m_desired_pos.dup();
};

gewCamera.prototype.getYaw = function() {
  return this.m_yaw;
};

gewCamera.prototype.getPitch = function() {
  return this.m_pitch;
};

gewCamera.prototype.getRoll = function() {
  return this.m_roll;
};

gewCamera.prototype.getLookAt = function() {
  return this.m_look_at_position.dup();
}

gewCamera.prototype.getDesiredOrient = function() {
  return this.m_desired_orient.dup();
};

gewCamera.prototype.getProjMatrix = function() {
  return this.m_project_matrix;
};

gewCamera.prototype.getViewMatrix = function() {
  return this.m_view_matrix;
};

gewCamera.prototype.getZoomFactor = function() {
  return this.m_zoom_factor;
};

gewCamera.prototype.setZoomFactor = function(factor) {
  this.m_zoom_factor = factor;
  this.setCameraFlag(CAMERA_UPDATE_PROJ_MATRIX, true);
};

gewCamera.prototype.updateViewMatrix = function() {
  this.m_forward = this.m_look_at_position.sub(this.m_desired_pos);
  this.m_forward.normalize();
  this.m_right = this.m_forward.cross(this.m_up);
  this.m_right.normalize();
  this.m_up = this.m_right.cross(this.m_forward);
  this.m_up.normalize();
  this.m_view_matrix = makeLookAt(this.m_desired_pos.x,
                                  this.m_desired_pos.y,
                                  this.m_desired_pos.z,
                                  this.m_look_at_position.x,
                                  this.m_look_at_position.y, // look_at_pos.y,
                                  this.m_look_at_position.z,
                                  this.m_up.x,
                                  this.m_up.y,
                                  this.m_up.z);
  this.setCameraFlag(CAMERA_UPDATE_VIEW_MATRIX, false);
};

gewCamera.prototype.calcZoom = function()
{
  //var aspect_ratio = this.m_viewport_size.x / this.m_viewport_size.y;
  var fovy = 2 * Math.atan(this.m_zoom_factor * Math.tan(this.m_fov*GEW_MATH_DEGREE_TO_RADIAN / 2));
  fovy /= GEW_MATH_DEGREE_TO_RADIAN;
  if (fovy < 0.0) fovy = 180 + fovy;
  
  this.m_desired_fov = fovy;
  
  this.setCameraFlag(CAMERA_UPDATE_PROJ_MATRIX, true);
}

gewCamera.prototype.updateProjMatrix = function() {
  this.m_project_matrix = makePerspective(this.m_desired_fov,
                                          //this.m_aspect_ratio,
                                          this.m_aspect_ratio,
                                          this.m_near,
                                          this.m_far);
  
  this.m_visual_project_matrix = makePerspective(this.m_desired_fov,
                                                //this.m_aspect_ratio,
                                                this.m_aspect_ratio,
                                                this.m_near,
                                                this.m_visual_far);
  this.setCameraFlag(CAMERA_UPDATE_PROJ_MATRIX, false);
};

gewCamera.prototype.prepareRender = function() {
  gewNode.prototype.prepareRender.call(this);
  if (this.getCameraFlag(CAMERA_UPDATE_VIEW_MATRIX)) {
    this.updateViewMatrix();
  }
  if (this.getCameraFlag(CAMERA_UPDATE_PROJ_MATRIX)) {
    this.updateProjMatrix();
  }
  if (this.getCameraFlag(CAMERA_UPDATE_VIEWPORT))
  {
    var gl = this.gl;
    var scene3d = this.scene3d;
    if (this === scene3d.camera_system.m_active_camera) {
      gl.viewport(this.m_viewport_pos.x, this.m_viewport_pos.y,
                  this.m_viewport_size.x, this.m_viewport_size.y);
      this.setCameraFlag(CAMERA_UPDATE_VIEWPORT, false);
    }
  }
};

// camera operators
/**
 * function pan 
 */
gewCamera.prototype.pan = function(vector){};
/**
 * Camera rotate
 * @param {Number} horizon - angle in degree
 * @param {Number} vertical - angle in degree
 */
gewCamera.prototype.rotate = function(horizon, vertical){};
/**
 * Camera zoom
 * @param {Number} delta
 */
gewCamera.prototype.zoom = function(delta)
{
  //this.calcZoom();
};

gewCamera.prototype.forward = function(delta)
{
  var forward = this.m_forward.mulScalar(delta);
  
	this.m_desired_pos = this.m_desired_pos.add(forward);
  this.m_look_at_position = this.m_look_at_position.add(forward);
  
  this.setCameraFlag(CAMERA_UPDATE_VIEW_MATRIX, true);
}

gewCamera.prototype.up = function(delta)
{
  var up = this.m_up.mulScalar(delta);
  this.m_desired_pos = this.m_desired_pos.add(up);
  this.m_look_at_position = this.m_look_at_position.add(up);
  
  this.setCameraFlag(CAMERA_UPDATE_VIEW_MATRIX, true);
}

gewCamera.prototype.side = function(delta)
{
  var side = this.m_right.mulScalar(delta);
  
  this.m_desired_pos = this.m_desired_pos.add(side);
  this.m_look_at_position = this.m_look_at_position.add(side);
  
  this.setCameraFlag(CAMERA_UPDATE_VIEW_MATRIX, true);
}

gewCamera.prototype.onMouseUp = function(e) 
{
  
}

gewCamera.prototype.onMouseDown = function(e)
{
  
}

gewCamera.prototype.onMouseMove = function(e)
{
  
}

gewCamera.prototype.onMouseWheel = function(e)
{  
}

gewCamera.prototype.onKeyDown = function(e)
{
  
}

gewCamera.prototype.onKeyUp = function(e)
{
  
}

gewCamera.prototype.onKeyPress = function(e)
{
  
}
